package com.ruoyi.mall.service.impl;

import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.ExceptionCodeConstants;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.DateCalcUtils;
import com.ruoyi.common.utils.SnowFlakeUtil;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.mall.domain.MallOrderDetail;
import com.ruoyi.mall.domain.bo.MallGoodsBo;
import com.ruoyi.mall.domain.bo.MallOrderBo;
import com.ruoyi.mall.domain.vo.CustomerVo;
import com.ruoyi.mall.domain.vo.MallGoodsVo;
import com.ruoyi.mall.service.*;
import com.ruoyi.mall.utils.StringUtils;
import com.ruoyi.mall.wechat.WechatPayDto;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class WechatServiceImpl implements IWechatService
{

//    @Resource
//    private JsapiService jsapiService;

//    @Resource
//    private RefundService refundService;

//    @Resource
//    private WechatPayConfig wechatPayConfig;

//    @Resource
//    private NotificationParser notificationParser;

    @Resource
    private ICustomerService customerService;

    @Resource
    private IMallOrderService mallOrderService;

    @Resource
    private IMallOrderDetailService mallOrderDetailService;

    @Resource
    private IMallGoodsService mallGoodsService;

    @Resource
    RedissonClient redissonClient;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public WechatPayDto orderPay(List<MallOrderDetail> lstMallOrderDetail)
    {
        LoginUser user = LoginHelper.getLoginUser();
        if (user == null)
        {
            throw new com.ruoyi.common.exception.ServiceException("账号未登录！", ExceptionCodeConstants.NO_LOGIN);
        }
        CustomerVo customerVo = customerService.queryById(user.getUserId());
        if (StringUtils.isNullOrEmpty(customerVo.getOpenId()))
        {
            throw new com.ruoyi.common.exception.ServiceException("用户没有微信唯一ID，无法支付！");
        }
        MallOrderBo mallOrderBo = new MallOrderBo();
        mallOrderBo.setCustomerId(customerVo.getCustomerId());
        mallOrderBo.setCustomerName(customerVo.getCustomerName());
        mallOrderBo.setScoreMallOrderId(SnowFlakeUtil.getDefaultSnowFlakeId().toString());
        mallOrderBo.setPayType(0l);
        mallOrderBo.setStatus(0l);
        mallOrderBo.setCreateTime(new Date());
        MallGoodsBo query = new MallGoodsBo();
        List<MallGoodsVo> lstGoods = mallGoodsService.queryList(query);
        boolean result = mallOrderService.insertByBo(mallOrderBo);
        if (result)
        {
            Long totalMoney = 0l;
            Long totalAmount = 0l;
            for (MallOrderDetail detail : lstMallOrderDetail)
            {
                detail.setScoreMallOrderId(mallOrderBo.getScoreMallOrderId());
                detail.setScoreMallOrderDetailId(SnowFlakeUtil.getDefaultSnowFlakeId().toString());
                MallGoodsVo goodsVo = lstGoods.stream().filter(item -> item.getGoodsId().equals(detail.getGoodsId())).findFirst().orElse(null);
                detail.setGoodsMoney(goodsVo.getGoodsMoney() * detail.getGoodsAmount());
                detail.setGoodsName(goodsVo.getGoodsName());
                detail.setCreateTime(new Date());
                totalMoney += goodsVo.getGoodsMoney() * detail.getGoodsAmount();
                totalAmount += detail.getGoodsAmount();
            }
            mallOrderBo.setTotalMoney(totalMoney);
            mallOrderBo.setTotalAmount(totalAmount);
            mallOrderDetailService.insertBatch(lstMallOrderDetail);
            try
            {
                RedisUtils.setCacheObject(CacheConstants.ORDER_TIME + customerVo.getPhone(), DateCalcUtils.addMinute(mallOrderBo.getCreateTime(), 15), Duration.ofMinutes(15));
                RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque("async-hand-msg");
                RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
                delayedQueue.offer(mallOrderBo.getScoreMallOrderId(), 15L, TimeUnit.MINUTES);
                String s = TimeUnit.SECONDS.toSeconds(15) + "分钟";
                log.info("添加延时队列成功 ，延迟时间：" + s);
            } catch (Exception e)
            {
                log.error(e.getMessage());
            }
            log.info("------消息已添加---------");
        }
        //todo 添加微信支付信息并返回给前端
        return null;
    }

//    /**
//     * js API 支付
//     * @param param 预支付参数
//     * @return 小程序唤起支付（金额已经确定了 输入密码的页面）
//     */
//    @Override
//    public WechatPayDto jsPay(WechatPayReqParam param) {
//        PrepayRequest prepayRequest = new PrepayRequest();
//        // 【公众账号ID】APPID是商户公众号唯一标识
//        prepayRequest.setAppid(wechatPayConfig.getAppId());
//        // 【商户号】是由微信支付系统生成并分配给每个商户的唯一标识符，
//        prepayRequest.setMchid(wechatPayConfig.getMchId());
//        // 【商户订单号】商户系统内部订单号，要求6-32个字符内，只能是数字、大小写字母_-|* 且在同一个商户号下唯一。
//        prepayRequest.setOutTradeNo(param.getOrderNumber());
//        // 【商品描述】商品信息描述，
//        prepayRequest.setDescription(param.getGoodsName());
//        /*
//         *【商户回调地址】商户接收支付成功回调通知的地址
//         * notify_url必须是以https:// 或 http:// 开头的完整全路径地址，
//         * 并且确保URL中的域名和IP是外网可以访问的，
//         * 不能填写localhost、127.0.0.1、192.168.x.x等本地或内网IP。
//         * 特别说明：
//         *          如果是http就在服务器的代理监听80端口
//         *          如果是https就在服务器的代理监听443端口
//         * 并且注意处理回调的时候controller中的接口 不要拦截 不要鉴权 否则微信调用不到。
//         * 也可以给回调通知的IP做白名单，详细的IP 参考：https://pay.weixin.qq.com/doc/v3/merchant/4012075420
//         */
//        prepayRequest.setNotifyUrl(wechatPayConfig.getCallbackUrl());
//        // 【订单金额】订单金额信息
//        Amount amount = new Amount();
//        // 【总金额】 订单总金额，单位为分，整型。 示例：1元应填写 100
//        amount.setTotal(Integer.valueOf(param.getTotalPrice()));
//        prepayRequest.setAmount(amount);
//        // 【支付者信息】支付者信息
//        Payer payer = new Payer();
//        // 【用户标识】用户在商户appid下的唯一标识。下单前需获取到用户的OpenID。
//        payer.setOpenid(param.getOpenId());
//        prepayRequest.setPayer(payer);
//
//        // 调用下单方法，得到应答
//        try {
//            PrepayResponse prepay = jsapiService.prepay(prepayRequest);
//            // 将 WechatPayDto 中的值返回小程序就可以唤起支付页面 也就是输入密码页面
//            WechatPayDto wechatPayDto = new WechatPayDto();
//            wechatPayDto.setAppid(wechatPayConfig.getAppId());
//            wechatPayDto.setTimeStamp(String.valueOf(System.currentTimeMillis() / 1000));
//            wechatPayDto.setNonceStr(NonceUtil.createNonce(32));
//            wechatPayDto.setPrepayId("prepay_id=" + prepay);
//            wechatPayDto.setSignType("RSA");
//            wechatPayDto.setPaySign(WeChatUtil.getSign(Stream.of(wechatPayConfig.getAppId(),
//                    wechatPayDto.getTimeStamp(),
//                    wechatPayDto.getNonceStr(),
//                    wechatPayDto.getPrepayId()).collect(Collectors.joining("\n", "", "\n")),
//                    wechatPayConfig.getKeyPath(),
//                    wechatPayConfig.getMchSerialNo()));
//            log.info("微信支付请求返回参数"+ JSONUtil.toJsonStr(wechatPayDto));
//            return wechatPayDto;
//        } catch (HttpException e) { // 发送HTTP请求失败
//            log.error("发送HTTP请求失败: " + e.getHttpRequest());
//            System.out.println("发送HTTP请求失败: " + e.getHttpRequest());
//        } catch (ServiceException e) { // 服务返回状态小于200或大于等于300，例如500
//            System.out.println("服务返回状态异常: "+ e.getResponseBody());
//        } catch (MalformedMessageException e) { // 服务返回成功，返回体类型不合法，或者解析返回体失败
//            System.out.println("返回体类型不合法: "+ e.getMessage());
//        } catch (Exception e) {
//            System.out.println("预下单异常: " + e.getMessage());
//        }
//        return null;
//    }
//
//    /**
//     * 支付的回调
//     * @param request request
//     * @param response response
//     * @return
//     */
//    @Override
//    public ResponseEntity.BodyBuilder payNotify(HttpServletRequest request, HttpServletResponse response) {
//        Transaction transaction;
//        try {
//            transaction = notificationParser.parse(WeChatUtil.handleNodifyRequestParam(request), Transaction.class);
//            if (transaction.getTradeState() == Transaction.TradeStateEnum.SUCCESS){
//                // 自己生成的订单号
//                String outTradeNo = transaction.getOutTradeNo();
//                //响应微信
//                return ResponseEntity.status(HttpStatus.OK);
//            }
//            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR);
//        } catch (ValidationException | IOException e) {
//            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
//            System.out.println("签名验证失败: " + e);
//            return ResponseEntity.status(HttpStatus.UNAUTHORIZED);
//
//        }
//
//    }
//
//    /**
//     * 申请退款
//     * @param param param
//     * @return
//     */
//    @Override
//    public String payRefund(WechatPayRefundReqParam param) {
//        CreateRequest request = new CreateRequest();
//        //设置退款金额 根据自己的实际业务自行填写
//        AmountReq amountReq = new AmountReq();
//        // 退款金额
//        amountReq.setRefund(Long.valueOf(param.getRefundMoney()));
//        // 支付时的金额
//        amountReq.setTotal(Long.valueOf(param.getTotalMoney()));
//        // 【退款币种】 符合ISO 4217标准的三位字母代码，固定传：CNY，代表人民币。
//        amountReq.setCurrency("CNY");
//        // 【金额信息】订单退款金额信息
//        request.setAmount(amountReq);
//        // 【商户退款单号】 商户系统内部的退款单号，商户系统内部唯一
//        request.setOutRefundNo("123");
//        // 【商户订单号】 商户下单时传入的商户系统内部订单号。订单号是自己内部业务生成的内部唯一
//        request.setOutTradeNo(param.getTransactionId());
//        //【退款结果回调url】退款的回调地址
//        request.setNotifyUrl(wechatPayConfig.getCallbackUrl());
//        // 提交退款申请成功，。
//        Refund refund = refundService.create(request);
//        // CreateTime 微信受理退款申请单的时间 退款是否成功不在这里判断，在退款回调中做处理。
//        return refund.getCreateTime();
//    }
//
//    /**
//     * 退款的回调
//     * 2.3、微信支付回调处理机制说明
//     * 微信支付接收到商户的应答后，会根据应答结果做对应的逻辑处理：
//     * 若商户应答回调接收成功，微信支付将不再重复发送该回调通知。若因网络或其他原因，商户收到了重复的回调通知，请按正常业务流程进行处理并应答。
//     * 若商户应答回调接收失败，或超时(5s)未应答时，微信支付会按照（15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h）的频次重复发送回调通知，直至微信支付接收到商户应答成功，或达到最大发送次数（15次）
//     * 所以 以防万一 最好在处理业务之前判断一下 是否已经处理了 如果已经出力了 就不在处理 直接响应微信就好了
//     */
//    @Override
//    public String refundNotify(HttpServletRequest request, HttpServletResponse response) {
//        Gson gson = new Gson();
//        final HashMap<String, Object> map = new HashMap<>();
//        try {
//            RefundNotification parse = notificationParser.parse(WeChatUtil.handleNodifyRequestParam(request), RefundNotification.class);
//            if ("SUCCESS".equals(parse.getRefundStatus())){
//                // 订单号
//                String outTradeNo = parse.getOutTradeNo();
//
//            } else {
//                // 退款失败要干嘛
//                // 订单号
//                String outTradeNo = parse.getOutTradeNo();
//            }
//            response.setStatus(200);
//            return null;
//        } catch (IOException e) {
//            // 通知验签失败
//            response.setStatus(500);
//            map.put("code", "ERROR");
//            map.put("message", "验签失败");
//            e.printStackTrace();
//            return gson.toJson(map);
//        }
//    }
}
